﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Common.DataAnnotations
{
    /// <summary>
    /// 分组属性中至少必填一项
    /// </summary>
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
    public class OneRequiredAttribute : ValidationAttribute
    {
        private static readonly ConcurrentDictionary<Type, Dictionary<string, List<PropertyInfo>>> cache = new ConcurrentDictionary<Type, Dictionary<string, List<PropertyInfo>>>();
        private readonly string groupName;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="group"></param>
        public OneRequiredAttribute(string group)
        {
            this.groupName = group;
        }
        /// <summary>
        /// 
        /// </summary>
        public override bool RequiresValidationContext => true;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="validationContext"></param>
        /// <returns></returns>
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var piList = GetGroupProperties(this.groupName, validationContext);
            ErrorMessage = $"{GetDisplayText(piList)}不允许同时为空";
            bool input = piList.Select(x => x.GetValue(validationContext.ObjectInstance)).Any(x => x != null);
            if (!input)
            {
                return new ValidationResult(FormatErrorMessage(groupName));
            }
            return null;
        }
        private static string GetDisplayText(List<PropertyInfo> properties)
        {
            var names = properties.Select(x => x.GetCustomAttribute<DisplayAttribute>()?.Name ?? x.Name);
            return string.Join("或", names);
        }
        private static List<PropertyInfo> GetGroupProperties(string groupName, ValidationContext validationContext)
        {
            var groupDict = cache.GetOrAdd(validationContext.ObjectType, gn =>
            {
                var allProperties = gn.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                Dictionary<string, List<PropertyInfo>> dict = new Dictionary<string, List<PropertyInfo>>();
                foreach (var p in allProperties)
                {
                    var oneList = p.GetCustomAttributes().OfType<OneRequiredAttribute>();
                    if (oneList != null && oneList.Any())
                    {
                        foreach (var one in oneList)
                        {
                            if (one != null)
                            {
                                if (!dict.ContainsKey(one.groupName))
                                {
                                    dict[one.groupName] = new List<PropertyInfo>();
                                }
                                dict[one.groupName].Add(p);
                            }
                        }
                    }
                }
                return dict;
            });
            return groupDict[groupName];
        }
    }
}
